/*
    This file is part of the substandard lisp implementation of Standard Lisp
    Copyright (C) 2011 Nicky Nickell

    substandard lisp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

module('interpreter core');

test('type checking', function () {
  ok(type_check(TYPE_INTEGER, CLASS_NUMBER), 'integer is number');
  ok(type_check(TYPE_FLOATING, CLASS_NUMBER), 'floating is number');
  ok(!type_check(TYPE_STRING, CLASS_NUMBER), 'string is not number');
  ok(!type_check(TYPE_ID, CLASS_NUMBER), 'id is not number');
  ok(!type_check(TYPE_PAIR, CLASS_NUMBER), 'pair is not number');
  ok(!type_check(TYPE_VECTOR, CLASS_NUMBER), 'vector is not number');
  ok(!type_check(TYPE_FUNCTION, CLASS_NUMBER), 'function is not number');
  ok(!type_check(TYPE_BOOLEAN, CLASS_NUMBER), 'boolean is not number');

  ok(type_check(TYPE_INTEGER, CLASS_CONSTANT), 'integer is constant');
  ok(type_check(TYPE_FLOATING, CLASS_CONSTANT), 'floating is constant');
  ok(type_check(TYPE_STRING, CLASS_CONSTANT), 'string is constant');
  ok(!type_check(TYPE_ID, CLASS_CONSTANT), 'id is not constant');
  ok(!type_check(TYPE_PAIR, CLASS_CONSTANT), 'pair is not constant');
  ok(type_check(TYPE_VECTOR, CLASS_CONSTANT), 'vector is constant');
  ok(type_check(TYPE_FUNCTION, CLASS_CONSTANT), 'function is constant');
  ok(!type_check(TYPE_BOOLEAN, CLASS_CONSTANT), 'boolean is not constant');

  ok(type_check(TYPE_INTEGER, CLASS_ANY), 'integer is any');
  ok(type_check(TYPE_FLOATING, CLASS_ANY), 'floating is any');
  ok(type_check(TYPE_STRING, CLASS_ANY), 'string is any');
  ok(type_check(TYPE_ID, CLASS_ANY), 'id is any');
  ok(type_check(TYPE_PAIR, CLASS_ANY), 'pair is any');
  ok(type_check(TYPE_VECTOR, CLASS_ANY), 'vector is any');
  ok(type_check(TYPE_FUNCTION, CLASS_ANY), 'function is any');
  ok(type_check(TYPE_BOOLEAN, CLASS_ANY), 'boolean is any');

  ok(type_check(TYPE_INTEGER, CLASS_ATOM), 'integer is atom');
  ok(type_check(TYPE_FLOATING, CLASS_ATOM), 'floating is atom');
  ok(type_check(TYPE_STRING, CLASS_ATOM), 'string is atom');
  ok(type_check(TYPE_ID, CLASS_ATOM), 'id is atom');
  ok(!type_check(TYPE_PAIR, CLASS_ATOM), 'pair is not atom');
  ok(type_check(TYPE_VECTOR, CLASS_ATOM), 'vector is atom');
  ok(type_check(TYPE_FUNCTION, CLASS_ATOM), 'function is atom');
  ok(type_check(TYPE_BOOLEAN, CLASS_ATOM), 'boolean is atom');

  ok(CLASS_EBOOLEAN == CLASS_ANY, 'eboolean is equivalent to any');
});

test('tokenizer', function () {
  deepEqual(tokenize('1'), ['1']);
  deepEqual(tokenize('123'), ['123']);
  deepEqual(tokenize('1.1'), ['1.1']);
  deepEqual(tokenize('0.1'), ['0.1']);
  deepEqual(tokenize('abc'), ['abc']);
  deepEqual(tokenize('"foo"'), ['"foo"']);
  deepEqual(tokenize('"foo\\""'), ['"foo\\""']);
  deepEqual(tokenize('\'foo'), ['\'', 'foo']);
  deepEqual(tokenize('#\'foo'), ['#\'', 'foo']);
  deepEqual(tokenize('\'#\'foo'), ['\'', '#\'', 'foo']);
  deepEqual(tokenize('()'), ['(', ')']);
  deepEqual(tokenize('(a b c)'), ['(', 'a', 'b', 'c', ')']);
  deepEqual(tokenize('(a (b c))'), ['(', 'a', '(', 'b', 'c', ')', ')']);
  deepEqual(tokenize('foo % bar'), ['foo']);
  deepEqual(tokenize('(foo % comment\n bar)'), ['(', 'foo', 'bar', ')']);
});

test('printer', function () {
  equal(print(T), 'T');
  equal(print(NIL), 'NIL');
  equal(print(new lisp_integer(BigInteger('1'))), '1');
  equal(print(new lisp_integer(BigInteger('123'))), '123');
  equal(print(new lisp_floating(Number('1.0'))), '1');
  equal(print(new lisp_floating(Number('0.1'))), '0.1');
  equal(print(new lisp_string('"foo"')), '"foo"');
  equal(print(new lisp_string('"foo\\"bar"')), '"foo"bar"');
  equal(print(new lisp_pair(new lisp_id('foo'), new lisp_id('bar'))), '(foo . bar)');
  equal(print(new lisp_pair(new lisp_id('foo'), new lisp_pair(new lisp_id('bar'), NIL))), '(foo bar)');
});

/*
 With build now requiring an environment as an argument, these tests cannot run.
 build will eventually be a method of interpreter so this will not be an issue in the future.

test('parser', function () {
  ok(build(['T']) == T);
  ok(build(['NIL']) == NIL);
  ok(build(['(', ')']) == NIL);
  deepEqual(build(['1']), new lisp_integer(BigInteger('1')));
  deepEqual(build(['1.1']), new lisp_floating(Number('1.1')));
  deepEqual(build(['foo']), new lisp_id('foo'));
  deepEqual(build(['"foo"']), new lisp_string('foo'));
  deepEqual(build(['"foo\\tbar"']), new lisp_string('foo\tbar'));
  deepEqual(build(['"foo\\nbar"']), new lisp_string('foo\nbar'));
  deepEqual(build(['"foo\\"bar"']), new lisp_string('foo"bar'));

  equal(print(build(tokenize('(foo bar)'))), '(foo bar)');
  equal(print(build(tokenize('\'foo'))), '(quote foo)');
  equal(print(build(tokenize('#\'foo'))), '(function foo)');
  equal(print(build(tokenize('(foo bar (biz buz))'))), '(foo bar (biz buz))');

  equal(print(build(tokenize('(1 . 2)'))), '(1 . 2)');
  equal(print(build(tokenize('(1 . (2 . (3 . (4 . NIL))))'))), '(1 2 3 4)');

  raises(function () {
    build([')'])
  }, /unexpected token '\)'/, 'unexpected ) while parsing');
  raises(function () {
    build([])
  }, /unexpected EOF/, 'unexpected EOF while parsing');
});
*/

var lisp = new interpreter();
lisp.initialize();

test('evaluate', function () {
  lisp.evaluate('(quote 1 2)');
  ok((lisp.exception == true) && /improper number of arguments/.test(lisp.result), '(quote 1 2) -r> improper number of arguments');
  lisp.evaluate('(compress 1)');
  ok((lisp.exception == true) && /\*\*\*\*\* 1 NOT/.test(lisp.result), '(compress 1) -r> improper argument type');
});
